﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fabs.Net.GIL;
using System.Xml;
using System.Reflection;

namespace Fabs.Net
{
    public class Parser
    {
        private string Code;

        public Parser(string Code)
        {
            this.Code = Code;
        }
        private List<Class> classList = new List<Class>();
        internal void Parse()
        {
            XmlDocument xml = new XmlDocument();
            xml.LoadXml(Code);
            XmlNodeList classes = xml.SelectNodes("/class");
            foreach (XmlNode cl in classes)
            {
                Class c = new Class();
                c.Name = cl.Attributes["name"].Value;
                if (cl.Attributes["public"] != null && cl.Attributes["public"].Value == "true")
                {
                    c.Attributes.Add(TypeAttributes.Public);
                }
                else if (cl.Attributes["internal"] != null && cl.Attributes["internal"].Value == "true")
                {
                    c.Attributes.Add(TypeAttributes.NestedAssembly);
                }
                else
                {
                    c.Attributes.Add(TypeAttributes.NotPublic);
                }

                if (cl.Attributes["static"] != null && cl.Attributes["static"].Value == "true")
                {
                    c.Attributes.Add(TypeAttributes.Abstract);
                    c.Attributes.Add(TypeAttributes.Sealed);
                }
                else
                {
                    if (cl.Attributes["abstract"] != null && cl.Attributes["abstract"].Value == "true")
                    {
                        c.Attributes.Add(TypeAttributes.Abstract);
                    }
                    if (cl.Attributes["sealed"] != null && cl.Attributes["sealed"].Value == "true")
                    {
                        c.Attributes.Add(TypeAttributes.Sealed);
                    }
                }

                XmlNodeList fields = cl.SelectNodes("/class/field");
                foreach (XmlNode field in fields)
                {
                    Variable v = new Variable();
                    v.Name = field.Attributes["name"].Value;
                    Type t = Type.GetType(field.Attributes["type"].Value);
                    v.Type = t;

                    if (field.Attributes["public"] != null && field.Attributes["public"].Value == "true")
                    {
                        v.Attributes.Add(FieldAttributes.Public);
                    }
                    else if (field.Attributes["internal"] != null && field.Attributes["internal"].Value == "true" && field.Attributes["protected"] != null && field.Attributes["protected"].Value == "true")
                    {
                        // v.Attributes.Add(FieldAttributes.as
                    }
                    else if (field.Attributes["internal"] != null && field.Attributes["internal"].Value == "true")
                    {
                        // v.Attributes.Add(FieldAttributes.as
                    }
                    else if (field.Attributes["protected"] != null && field.Attributes["protected"].Value == "true")
                    {
                        //  v.Attributes.Add(FieldAttributes.
                    }
                    else
                    {
                        v.Attributes.Add(FieldAttributes.Private);
                    }

                    if (field.Attributes["static"] != null && field.Attributes["static"].Value == "true")
                    {
                        v.Attributes.Add(FieldAttributes.Static);
                    }

                    if (field.Attributes["value"] != null)
                    {
                        v.Value = field.Attributes["value"].Value;
                    }

                    c.Variables.Add(v);
                }


                XmlNodeList functions = cl.SelectNodes("/class/function");
                foreach (XmlNode function in functions)
                {
                    string name = function.Attributes["name"].Value;
                    if (name == c.Name)
                    {
                        Constructor cn = new Constructor();
                        int argcount = 0;
                        while (function.Attributes["arg" + argcount] != null)
                        {
                            Type type = Type.GetType(function.Attributes["arg" + argcount].Value);
                            cn.ParameterTypes.Add(type);
                            argcount++;
                        }

                        if (function.Attributes["public"] != null && function.Attributes["public"].Value == "true")
                        {
                            cn.Attributes.Add(MethodAttributes.Public);
                        }
                        else if (function.Attributes["internal"] != null && function.Attributes["internal"].Value == "true" && function.Attributes["protected"] != null && function.Attributes["protected"].Value == "true")
                        {
                            //f.Attributes.Add(MethodAttributes.in
                        }
                        else if (function.Attributes["internal"] != null && function.Attributes["internal"].Value == "true")
                        {
                            // v.Attributes.Add(FieldAttributes.as
                        }
                        else if (function.Attributes["protected"] != null && function.Attributes["protected"].Value == "true")
                        {
                            //  v.Attributes.Add(FieldAttributes.
                        }
                        else
                        {
                            cn.Attributes.Add(MethodAttributes.Private);
                        }

                        if (function.Attributes["static"] != null && function.Attributes["static"].Value == "true")
                        {
                            cn.Attributes.Add(MethodAttributes.Static);
                        }

                        ParseCode(function, cn);
                        c.Constructors.Add(cn);
                    }
                    else
                    {
                        Function f = new Function();
                        f.Name = name;
                        string t = function.Attributes["type"] != null ? function.Attributes["type"].Value : "void";
                        Type ty = Type.GetType(t);
                        f.ReturnType = ty;

                        int argcount = 0;
                        while (function.Attributes["arg" + argcount] != null)
                        {
                            Type type = Type.GetType(function.Attributes["arg" + argcount].Value);
                            f.ParameterTypes.Add(type);
                            argcount++;
                        }

                        if (function.Attributes["public"] != null && function.Attributes["public"].Value == "true")
                        {
                            f.Attributes.Add(MethodAttributes.Public);
                        }
                        else if (function.Attributes["internal"] != null && function.Attributes["internal"].Value == "true" && function.Attributes["protected"] != null && function.Attributes["protected"].Value == "true")
                        {
                            //f.Attributes.Add(MethodAttributes.in
                        }
                        else if (function.Attributes["internal"] != null && function.Attributes["internal"].Value == "true")
                        {
                            // v.Attributes.Add(FieldAttributes.as
                        }
                        else if (function.Attributes["protected"] != null && function.Attributes["protected"].Value == "true")
                        {
                            //  v.Attributes.Add(FieldAttributes.
                        }
                        else
                        {
                            f.Attributes.Add(MethodAttributes.Private);
                        }

                        if (function.Attributes["static"] != null && function.Attributes["static"].Value == "true")
                        {
                            f.Attributes.Add(MethodAttributes.Static);
                        }

                        ParseCode(function, f);
                        c.Functions.Add(f);
                    }
                }
                classList.Add(c);
            }
        }

        private void ParseCode(XmlNode function, IFunction f)
        {
            XmlNodeList nodes = function.ChildNodes;
            for (int i = 0; i < nodes.Count; i++)
            {
                XmlNode node = nodes[i];
                switch (node.Name)
                {
                    case "set":
                        {
                            string field = node.Attributes["field"].Value;
                            string value = node.Attributes["value"].Value;
                            f.SetValue(field, value);
                        }
                        break;
                    case "write":
                        {
                            string value = node.Attributes["value"].Value;
                            List<string> args = new List<string>();

                            int argcount = 0;
                            while (node.Attributes["arg" + argcount] != null)
                            {
                                args.Add(node.Attributes["arg" + argcount].Value);
                                argcount++;
                            }
                            f.Write(value, args);
                        }
                        break;
                }
            }
        }

        internal void Write(Executable Output)
        {
            Output.Classes.AddRange(classList);
        }
    }
}
